/* Copyright 2009 Sony Mathew, Minneapolis MN. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at  http://www.apache.org/licenses/LICENSE-2.0  Unless required by applicable law or agreed to in writing, software  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */
package com.xsm.gwt.remoteevent.client;

import java.util.HashMap;
import java.util.Map;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.google.gwt.user.client.rpc.StatusCodeException;
import com.xsm.gwt.widgets.client.GWTUtil;
import com.xsm.lite.event.CategoryEvent;
import com.xsm.lite.event.EventManager;
import com.xsm.lite.event.RemoteFailureEvent;
import com.xsm.lite.event.RemoteRequestEvent;
import com.xsm.lite.event.RemoteResponseEvent;
import com.xsm.lite.event.RemoteSessionTimeoutEvent;

/**
 * Slurps up RemoteRequestEvents on client side and serializes them to the server side.
 * 
 * @author Sony Mathew
 */
public class RemoteEventProxyClient implements CategoryEvent.Listener {

    //The extra path info on the server-url that processes remote events.
    public static final String SERVER_URL_PATH_INFO = "/remote/event";    
    
	public static final String MESSAGE_SESSION_TIMEOUT = "Session Timeout";
    
    private final EventManager em; 
    private RemoteEventProxyAsync remoteEventProxyAsync = null;
    private final Map<String, RemoteResponseEvent> responseCache = new HashMap<String, RemoteResponseEvent>();
    private String serverUrlPathInfo = SERVER_URL_PATH_INFO;
    
    /**
     * Provide the EventManager used for listening to RemoteRequestEvents that require processing 
     *  as well as for firing RemoteResponseEvents when they are received.
     *  
     * By default uses constant: SERVER_URL_PATH_INFO on the current server URL to locate the Remote Event service.
     *  Uses GWTUtil.toFullURL(SERVER_URL_PATH_INFO).
     *  
     * author Sony Mathew
     */
    public RemoteEventProxyClient(EventManager em) {
        this.em = em;
        em.addCategoryListener(RemoteRequestEvent.class, this);
    }

    /**
     * Provide an override URL path info when accessing the server.
     *  Uses GWTUtil.toFullURL(overrideServerUrlPathInfo).
     *  
     * author Sony Mathew
     */
    public void overrideServerUrlPathInfo(String override) {
        if (override == null || (override=override.trim()).length()==0) {
            throw new IllegalStateException("Invalid server url path info override provided [" + override + "]");
        }
        
        serverUrlPathInfo = override;
    }
    
    /**
     * @see com.xsm.lite.event.CategoryEvent.Listener#onEvent(com.xsm.lite.event.CategoryEvent)
     */   
    public void onEvent(CategoryEvent cate) {
        final RemoteRequestEvent reqe = (RemoteRequestEvent)cate.getEvent();
        RemoteResponseEvent respe = responseCache.get(reqe);
        if (respe != null) {
            em.fire(respe);
            return;
        }
        
        getRemoteEventProxy().process(reqe, new AsyncCallback<RemoteResponseEvent>() {
            public void onSuccess(RemoteResponseEvent result) {
                if (result.canCache()) {
                    responseCache.put(result.getRequestKey(), result);
                }
                em.fire(result);
            }            
            public void onFailure(Throwable x) {
            	GWT.log("Remote call failed for: " + reqe.toString(), x);
            	if (x instanceof StatusCodeException) {
            		//	don't process session timeout. They are handled by SessionTimeoutPanel
            		if (MESSAGE_SESSION_TIMEOUT.equals(x.getMessage())) {
            			em.fire(new RemoteSessionTimeoutEvent(reqe));
            			return;
            		}
            	}

                em.fire(new RemoteFailureEvent(reqe, x));
            }            
        });
    }

    /**
     * Obtain the remote control.
     * 
     * author Sony Mathew
     */
    public RemoteEventProxyAsync getRemoteEventProxy() {
        if (remoteEventProxyAsync != null) {
            return remoteEventProxyAsync;
        }

        remoteEventProxyAsync = (RemoteEventProxyAsync)GWT.create(RemoteEventProxy.class);
        ServiceDefTarget endpoint = (ServiceDefTarget)remoteEventProxyAsync;
        String remoteEventProxyAsyncURL = GWTUtil.toFullURL(serverUrlPathInfo);
        endpoint.setServiceEntryPoint(remoteEventProxyAsyncURL);
        return remoteEventProxyAsync;
     }
    
}
